import numpy as np
from sklearn.decomposition import PCA
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.cluster import KMeans

class DimensionReduction:

    @staticmethod
    def dimensionReduction(center, dataset, n_components):
        '''
            This method conduct the dimensionality reduction process by principle component analysis,
            which transform the data to 2D data,
        '''
        dataACen = np.concatenate((center, dataset), axis=0)
        pca = PCA(n_components)
        pca.fit(dataACen)
        dataACen_dr = pca.fit_transform(dataACen)
        center_dr = dataACen_dr[:center.shape[0], :]
        data_dr = dataACen_dr[center.shape[0]:, :]

        return center_dr, data_dr

    @staticmethod
    def decomposition_MutliVari(tolist, n_components, *args):
        n = len(args)
        com = args[0]
        #
        # for i in range(n):
        #     print(type(args[i]))

        for i in range(1, n):
            # print(args[i])
            com = np.concatenate((com, args[i]), axis=0)

        # print(com)
        pca = PCA(n_components)
        pca.fit(com)
        com_dr = pca.fit_transform(com)

        retlist = []
        length_old, length_new = 0, 0

        for i in range(n):
            length_new += len(args[i])
            retlist.append(com_dr[length_old:length_new, :].tolist())
            length_old = length_new


        if tolist:
            for i in range(len(retlist)):
                retlist[i] = np.array(retlist[i])

            retlist = np.array(retlist)
            if len(retlist) == 1:
                return retlist[0]
            else:
                return (item for item in retlist)
        else:
            if len(retlist) == 1:
                return retlist[0]
            else:
                return (item for item in retlist)

    @staticmethod
    def dimensionReduction_LDA(center, dataset, n_components, label):
        dataACen = np.concatenate((center, dataset), axis=0)
        labelACen = np.concatenate((np.arange(label.max()), label), axis=0)
        lda = LinearDiscriminantAnalysis(n_components)
        lda.fit(dataACen, labelACen)
        dataACen_dr = lda.fit_transform(dataACen, labelACen)
        center_dr = dataACen_dr[:center.shape[0], :]
        data_dr = dataACen_dr[center.shape[0]:, :]

        return center_dr, data_dr

    @staticmethod
    def split_list(data, n=None):

        templist = []

        if n == None:
            n = len(data)

        for i in range(n):
            for j in range(len(data[i])):
                templist.append(data[i][j])

        return templist, n

    @staticmethod
    def de_split_list(data, nob):

        m = len(data) // nob
        s = 0
        e = m

        result = []

        for i in range(nob):
            result.append(data[s:e])
            e = s
            e += m

        return result

    @staticmethod
    def split_data(n_component, dataset, center):
        model = KMeans(n_clusters=n_component, init=np.array(center))

        model.fit(dataset)
        a = model.labels_
        # print(model.cluster_centers_)
        idx = []
        for i in range(n_component):
            tmp = np.where(a == i)[0].tolist()
            idx.append(tmp)

        # for i in range(model.n_clusters):
        #     print(type(idx[i]))
        data = []
        # print(idx)
        dataarr = np.array(dataset)
        for i in range(model.n_clusters):
            data.append(dataarr[idx[i]].tolist())

        return data

    @staticmethod
    def split_data_reidx(n_component, dataset, center):
        model = KMeans(n_clusters=n_component, init=np.array(center))

        model.fit(dataset)
        a = model.labels_
        # print(model.cluster_centers_)
        idx = []
        for i in range(n_component):
            tmp = np.where(a == i)[0].tolist()
            idx.append(tmp)

        return idx, model.cluster_centers_

    @staticmethod
    def splitdata(idx, dataset):

        data = []

        for i in range(len(idx)):
            data.append(np.array(dataset)[idx[i]].tolist())

        return data